在上篇中,介绍了什么是App分屏模式,以及如何设置我们的App来进入分屏模式。这次我们看一下,作为开发者,我们应该如何让自己的App进入分屏模式,当App进入分屏模式时,我们注意哪些问题。
简单地说,我认为除了保证分屏时App功能、性能正常以外,我们需要重点学习 如何在分屏模式下打开新的Activity 以及 如何实现跨App/Activity的拖拽功能。
用分屏模式运行你的App
Android N中新增了一些方法来支持App的分屏模式。同时在分屏模式下,也禁用了App一些特性。
分屏模式下被禁用的特性
- 自定义系统UI,例如分屏模式下无法隐藏系统的状态栏。
- 无法根据屏幕方向来旋转App的界面,也就是说
android:screenOrientation
属性会被系统忽略。
分屏模式的通知回调、查询App是否处于分屏状态
最新的Android N SDK中,Activity
类中增加了下面的方法。
- inMultiWindow():返回值为boolean,调用此方法可以知道App是否处于分屏模式。
- inPictureInPicture():返回值为boolean,调用此方法可以知道App是否处于画中画模式。
注意:
画中画模式
其实是一个特殊的分屏模式
,如果mActivity.inPictureInPicture()
返回true
,那么mActivity.inMultiWindow()
一定也是返回true
。
- onMultiWindowChanged(boolean inMultiWindow):当Activity进入或者退出分屏模式时,系统会回调这个方法来通知开发者。回调的参数
inMultiWindow
为boolean类型,如果inMultiWindow
为true,表示Activity进入分屏模式;如果inMultiWindow
为false,表示退出分屏模式。 - onPictureInPictureChanged(boolean inPictureInPicture):当Activity进入画中画模式时,系统会回调这个方法。回调参数
inPictureInPicture
为true
时,表示进入了画中画模式;inPictureInPicture
为false
时,表示退出了画中画模式。
Fragment
类中,同样增加了以上支持分屏模式的方法,例如Fragment.inMultiWindow()
。
如何进入画中画模式
调用Activity
类的enterPictureInPicture()
方法,可以使得我们的App进入画中画模式。如果运行的设备不支持画中画模式,调用这个方法将不会有任何效果。更多画中画模式的资料,请参考picture-in-picture。
在分屏模式下打开新的Activity
当你打开一个新的Activity时,只需要给Intent添加Intent.FLAG_ACTIVITY_LAUNCH_TO_ADJACENT
,系统将尝试将它设置为与当前的Activity共同以分屏的模式显示在屏幕上。
注意:这里只是尝试,但这不一定是100%生效的,前一篇博客里也说过,假如新打开的Activity的android:resizeableActivity
属性设置为false
,就会禁止分屏浏览这个Activity。所以系统只是尝试去以分屏模式打开一个新的Activity,如果条件不满足,将不会生效!此外,我实际用Android N Preview SDK
实践的时候发现这个FLAG
实际得值是FLAG_ACTIVITY_LAUNCH_ADJACENT
,并非是FLAG_ACTIVITY_LAUNCH_TO_ADJACENT
。
当满足下面的条件,系统会让这两个Activity进入分屏模式:
- 当前Activity已经进入到分屏模式。
- 新打开的Activity支持分屏浏览(即android:resizeableActivity=true)。
此时,给新打开的Activity,设置intent.addFlags(Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT | Intent.FLAG_ACTIVITY_NEW_TASK);
才会有效果。
建议参考官方的Sample:MultiWindow Playground Sample
那么为何还需要添加FLAG_ACTIVITY_NEW_TASK
?看一下官方解释:
注意:在同一个Activity返回栈中,打开一个新的Activity时,这个Activity将会继承上一个Activity所有和
分屏模式
有关的属性。如果你想要在一个独立的窗口以分屏模式打开一个新的Activity,那么必须新建一个Activity返回栈。
此外,如果你的设备支持自由模式
(官方名字叫freeform,暂且就这么翻译它,其实我认为这算也是一种尺寸更自由的分屏模式,上一篇博客里提到过如果设备厂商支持用户可以自由改变Activity的尺寸,那么就相当于支持自由模式
,这将比普通的分屏模式更加自由),打开一个Activity时,还可通过ActivityOptions.setLaunchBounds()
来指定新的Activity的尺寸和在屏幕中的位置。同样,这个方法也需要你的Activity已经处于分屏模式时,调用它才会生效。
支持拖拽
在上一篇博客里也提到过,现在我们可以实现在两个分屏模式的Activity之间拖动内容了。Android N Preview SDK中,View
已经增加支持Activity之间拖动的API。具体的类和方法,可以参考N Preview SDK Reference,主要用到下面几个新的接口:
- View.startDragAndDrop():View.startDrag()) 的替代方法,需要传递
View.DRAG_FLAG_GLOBAL
来实现跨Activity拖拽。如果需要将URI权限传递给接收方Activity,还可以根据需要设置View.DRAG_FLAG_GLOBAL_URI_READ
或者View.DRAG_FLAG_GLOBAL_URI_WRITE
。 - View.cancelDragAndDrop():由拖拽的发起方调用,取消当前进行中的拖拽。
- View.updateDragShadow():由拖拽的发起方调用,可以给当前进行的拖拽设置阴影。
- android.view.DropPermissions:接收方App所得到的权限列表。
- Activity.requestDropPermissions():传递URI权限时,需要调用这个方法。传递的内容存储在DragEvent中的ClipData里。返回值为前面的
android.view.DropPermissions
。
下面是我自己写的一个demo,实现了在分屏模式下,把一个Activity中ImageView中保存的内容到另外一个Activity中进行显示。实际应用中,可以还可以传递图片的url或者Bitmap对象。
上图是一个最基本的例子,实现了把MainActivity中的图片保存的内容,拖拽到SecondActivity中。实现步骤如下:
在MainActivity中,发起拖拽。
|
|
在SecondActivity
中,接收这个拖拽的结果,在ACTION_DROP
事件中,把结果显示出来。
|
|
这里实现的关键在新增加的startDragAndDrop
方法,看下官方的API文档:
清楚地提到了,发出的DragEvent能够被所有可见的View对象接收到
,所以在分屏模式下,SecondActivity可以监听View的onDrag事件,于是我们监听它!
接着,我们看下DragEvent.ACTION_DROP
事件发生的条件:
当被拖拽的View的阴影进入到接收方View的坐标区域,如果此时用户松手,那么接收方View就可以接收到这个Drop事件。一目了然,我们通过拖拽ImageView到图上的灰色区域,松手,便可以触发DragEvent.ACTION_DROP
,把数据传到SecondActivity中了。
其实还有更复杂的一些情况,需要调用requestDropPermissions
,后续我再进一步实践一下。
这个demo的地址在这里,先分享出来,后面我再接着完善它。
在分屏模式下测试你的App
无论你是否将自己的App适配到了Android N,或者是支持分屏模式,都应该找个Android N的设备,来测试一下自己的App在分屏模式下会变成什么样。
设置你的测试设备
如果你有一台运行Android N的设备,它是默认支持分屏模式的。
如果你的App不是用Android N Preview SDK打包的
如果你的App是用低于Android N Preview SDK
打包的,且你的Activity支持横竖屏切换
。那么当用户在尝试使用分屏模式时,系统会强制将你的App进入分屏模式。(我在第一篇博客里提到过这个,Android N Preview的介绍视频中,很多Google家的App都可以进入分屏模式,但是打开它们的xml一看,其实targetSDKVersion = 23
)
因此,如果你的App/Activity支持横竖屏切换,那么你应该尝试一下让自己的App分屏,看看当系统强制改变你的App尺寸时,用户是否还可以接受这种体验。如果你的App/Activity不支持横竖屏切换,那么你可以确认一下,看看当尝试进入分屏时,你的App是不是仍然能够保持全屏模式。
如果你给App设置了支持分屏模式
如果你使用了Android N Preview SDK
来开发自己的App,那么应该按照下面的要点检查一下自己的App。
- 启动App,长按系统导航栏右下角的小方块(Google官方把这个叫做Overview Button),确保你的App可以进入分屏模式,且尺寸改变后仍然能正常工作。
- 启动任务管理器(即单击右下角的小方块),然后长按你App的标题栏,将它拖动到屏幕上的高亮区域。确保你的App可以进入分屏模式,且尺寸改变后仍然能正常工作。
这两点在上一篇博客中介绍过,让自己的App进入分屏模式有三种方法。第三种方法,就是在打开自己的App时,用手指从右下角的小方块向上滑动,这样也可以使得正在浏览的App进入分屏模式。这种方法目前属于实验性功能,正式版不一定保留。
- 当你的App进入分屏后,通过拖动两个App中间的分栏上面的小白线,从而改变App的尺寸,观察App中各个UI元素是否正常显示。
- 如果你给自己的App/Activity设置了最小尺寸,可以尝试在改变App尺寸时,低于这个最小尺寸,观察App是不是会回到设定好的最小尺寸。
- 在进行上面几项测试时,请同时验证自己的App功能和性能是否正常,并注意一下自己的App在更新UI时是否花费了太长的时间。
这几项测试,其实主要强调的是,我们的App可以顺利的进入/退出分屏模式,且改变App的尺寸时,UI依然可以也非常顺滑。
这里我想多说一句,如果进入了分屏模式,要注意下App弹出的对话框,因为屏幕被两个App分成两块之后,对话框也是可以弹出两个的。这时对话框上的UI元素可能就会变得比较小了,如果我们的代码是写死的大小,例如对话框是一个WebView,就需要特别注意了,搞不好显示出来就缺了一块了,这里需要我们做好适配。
测试清单
关于功能、性能方面测试,还可以按照下面的操作来进行。
- 让App进入,再退出分屏模式,确保此时App功能正常。
- 让App进入分屏模式,激活屏幕上的另外一个App,让自己的App进入
可见、paused
状态。举了例子来讲,如果你的App是一个视频播放器,那么当用户点击了屏幕上另外一个App时,你的App不应该停止播放视频,即使此时你的Activity/Fragment已经接到了onPaused()
回调。 - 让App进入分屏模式,拖动分栏上的小白线,改变App的尺寸。请在竖屏(两个App一上一下布局)和横屏(两个App一左一右布局)模式下分别进行改变尺寸的操作。确保App不会崩溃,各项功能正常,且UI的刷新没有花费太多时间。
- 在短时间内、多次、迅速地改变App尺寸,确保App没有崩溃,且没有发生内存泄露。关于内存使用方面的更详细注意事项,请参考Investigating Your RAM Usage。
- 在不同的窗口设置的情况下,正常使用App,确保App功能正常,文字仍然可读,其他的UI元素也没有变得太小,用户仍然可以舒适地操作App。
这几项测试,其实主要是说当App在分屏模式下运行时,仍然可以保持性能的稳定,不会Crash也不会OOM。
如果你给App设置了禁止分屏模式
如果你给App/Activity设置了android:resizableActivity="false"
,你应该试试当用户在Android N的设备上,尝试分屏浏览你的App时,它是否仍然能保持全屏模式。
以上就是参考Google最新的multi-window进行的实践,总结下,我认为有3点比较重要:
- 如何让自己的App/Activity顺利的进入和退出分屏模式,可以参考处理运行时改变这一章。
- 如何在分屏模式下打开新的Activity,可以参考Google官方的MultiWindow Playground Sample。
- 如何实现跨App/Activity的拖拽功能,可以参考Drag and Drop这一章。
关于App分屏模式的学习就到这里了,欢迎大家一起交流。我们还发挥更多的想象力,比如是否可以利用跨应用拖拽实现更方便操作,更好的用户体验。